package block

import (
	"errors"
	"fmt"
	"github.com/boltdb/bolt"
)


const CHAIN_DB_PATH = "./chain.db"
const BUKET_BLOCK = "chain_block"
const BUKET_STATUS = "chain_status"


type BlockChain struct {
	DB  *bolt.DB
	LastHash []byte
}

func CreateChain(data []byte)(*BlockChain,error){
	var lashhash []byte
	db, err := bolt.Open(CHAIN_DB_PATH, 0600, nil)
	if err !=nil{
		return nil,err
	}
	//向数据库中添加数据
	err = db.Update(func(tx *bolt.Tx) error {
		//找桶1，为了存区块信息
		bk := tx.Bucket([]byte(BUKET_BLOCK))
		//没有桶1
		if bk == nil{

			//创建桶1
			bk, err := tx.CreateBucket([]byte(BUKET_BLOCK))
			if err !=nil{
				return err
			}
			//开始添加
			genesis:=CreateGenesis(data)
			//把创世区块转成[]byte   serialize
			serialize, err := genesis.Serialize()
			bk.Put(genesis.Hash,serialize)
			//创建桶2，为了存储最后一个区块的hash
			bk2, _ := tx.CreateBucket([]byte(BUKET_STATUS))
			if err != nil {
				return nil
			}
			bk2.Put([]byte("LAST_HASH"),genesis.Hash)
			lashhash = genesis.Hash
		}else{
			bk2 := tx.Bucket([]byte(BUKET_STATUS))
			hash := bk2.Get([]byte("LAST_HASH"))
			lashhash = hash
		}
		return nil
	})
	bc:=BlockChain{
		DB: db,
		LastHash:lashhash,
	}

	return &bc,err
}
//向区块链中添加新区块的
func (bc *BlockChain)AddBlock(data []byte)error{
	newBlock:=CreateBlock(data,bc.LastHash)

	err :=bc.DB.Update(func(tx *bolt.Tx) error {
		bk:= tx.Bucket([]byte(BUKET_BLOCK))
		if bk == nil{
			return errors.New("没有桶，无法添加")
		}
		newBytes, err := newBlock.Serialize()
		if err !=nil {
			return err
		}
		//存数据
		bk.Put(newBlock.Hash,newBytes)


		bk2 := tx.Bucket([]byte(BUKET_STATUS))
		if bk2 == nil {
			return errors.New("没有桶2")
		}
		bk2.Put([]byte("LAST_HASH"),newBlock.Hash)
		bc.LastHash = newBlock.Hash
		return nil
	})
	return err
}
//为了创建迭代器
func (bc *BlockChain)Iterator()*ChainIterator {
	iterator:= ChainIterator{
		DB: bc.DB,
		CurrentHash: bc.LastHash,
	}
	return &iterator


}
//获取区块链中的所有区块对象
func(bc *BlockChain)GetAllBlock()[]*Block{
	blocks :=[]*Block{}
	iterator := bc.Iterator()
	for iterator.HasNext() {
		block, err := iterator.Next()
		if err !=nil {
			fmt.Println(err.Error())
			return nil
		}
		blocks=append(blocks,block)
	}
	return blocks
}